
typedef struct{
    int fd;
    int readlen;
    int offset;
    int isend;
    u8  buf[1024];
    int buf_size;
}fd_read_t;

static int get_hook(char*buf,u32*addr,char**path);
uint8_t __attribute__ ((aligned(32))) data_buf[1024];
u8  text_buf[1024*1024];

static int fd_readbuf_sz(int fd,u8*dest,int sz){
    u8 *buf = data_buf;
    int off = 0;
    while(sz>1024){
        int ret = IOS_Read(fd,buf,1024);
        if(ret){
            memcpy(dest+off,data_buf,ret);
            off += ret;
        }
        if(ret<sz){
            return off;
        }
        off += 1024;
        sz-=1024;
    }

    int ret = IOS_Read(fd,buf,1024);
    if(ret){
        memcpy(dest,data_buf,ret);
    }
    off += ret;
    return off;
}

static int fd_writebuf(int fd,u8*src,int sz){
    u8*buf = data_buf;
    do{
        int w_size = 1024;
        if(sz<1024){
            w_size = sz;
        }
        memcpy(buf,src,w_size);
        IOS_Write(fd,buf,w_size);
        src += w_size;
        sz -= w_size;
    }while(sz>0);
    return 0;
}

static int fd_readbuf(int fd,u8*dest){
    //u8 buf[1024];
    if(((u32)dest&0x1f)==0){
        int off = 0;
        //数据对齐，可以直接加载数据
        do{
            int rlen = IOS_Read(fd,dest+off,1024);
            if(rlen>0)
                off += rlen;
            if(rlen<=1024){
                break;
            }
        }while(1);
        return off;
    }
    u8 *buf = data_buf;
    int sz = 0;
    int readlen;
    do{
        readlen = IOS_Read(fd,buf,1024);
        if(readlen>0){
            memcpy(dest,buf,readlen);
        }

        if(readlen<1024) return 0;
        sz += readlen;
    }while(1);
    return sz + readlen;
}

static int fd_setpos(fd_read_t*fd,int pos){
    return IOS_Seek(fd,pos,0);
}

static int fd_gets(fd_read_t*fd,u8*buf){
    if(fd->isend) return 0;
    int off = 0;
    while(1){
        if(fd->offset>=fd->readlen || fd->readlen==0){
            fd->readlen = fd_readbuf_sz(fd->fd,fd->buf,1024);
            if(fd->readlen<=0){
                buf[off++] = 0;
                fd->isend = 1;
                break;
            }
            fd->offset = 0;
        }
        int ch = fd->buf[fd->offset++];
        if(ch=='\n'){
            buf[off++] = 0;
            break;
        }
        buf[off++] = ch;
    }
    return off;
}

static int skip(char*buf,int offset){
    while(buf[offset]==' '||buf[offset]=='\t'){
        offset++;
    }
    return offset;
}

const u8 itoh[0x10] = {
    [0] = '0',
    [1] = '1',
    [2] = '2',
    [3] = '3',
    [4] = '4',
    [5] = '5',
    [6] = '6',
    [7] = '7',
    [8] = '8',
    [9] = '9',
    [10] = 'a',
    [11] = 'b',
    [12] = 'c',
    [13] = 'd',
    [14] = 'e',
    [15] = 'f',

};

const u8 hex[0x100] = {
    ['0'] = 1+0,
    ['1'] = 1+1,
    ['2'] = 1+2,
    ['3'] = 1+3,
    ['4'] = 1+4,
    ['5'] = 1+5,
    ['6'] = 1+6,
    ['7'] = 1+7,
    ['8'] = 1+8,
    ['9'] = 1+9,
    ['a'] = 1+0xa,
    ['b'] = 1+0xb,
    ['c'] = 1+0xc,
    ['d'] = 1+0xd,
    ['e'] = 1+0xe,
    ['f'] = 1+0xf,
    ['A'] = 1+0xa,
    ['B'] = 1+0xb,
    ['C'] = 1+0xc,
    ['D'] = 1+0xd,
    ['E'] = 1+0xe,
    ['F'] = 1+0xf,
};


/*
用类似setjmp,longjmp的方法
*/
void __hook_main_asm(){
nasm(".section	\".text\"");
nasm(".align 2");
nasm(".globl hook_main_asm");
nasm(".type	hook_main_asm, @function");
nasm("hook_main_asm:");
//加载 0x8fff0000 位置
nasm("mr 0,3");
nasm("lis 3,0x80ff");
nasm("stw 1,4*1   (3)");
nasm("stw 2,4*2   (3)");
nasm("stw 0,4*3   (3)");
nasm("stw 4,4*4   (3)");
nasm("stw 5,4*5   (3)");
nasm("stw 6,4*6   (3)");
nasm("stw 7,4*7   (3)");
nasm("stw 8,4*8   (3)");
nasm("stw 9,4*9   (3)");
nasm("stw 10,4*10 (3)");
nasm("stw 11,4*11 (3)");
nasm("stw 12,4*12 (3)");
nasm("stw 13,4*13 (3)");
nasm("stw 14,4*14 (3)");
nasm("stw 15,4*15 (3)");
nasm("stw 16,4*16 (3)");
nasm("stw 17,4*17 (3)");
nasm("stw 18,4*18 (3)");
nasm("stw 19,4*19 (3)");
nasm("stw 20,4*20 (3)");
nasm("stw 21,4*21 (3)");
nasm("stw 22,4*22 (3)");
nasm("stw 23,4*23 (3)");
nasm("stw 24,4*24 (3)");
nasm("stw 25,4*25 (3)");
nasm("stw 26,4*26 (3)");
nasm("stw 27,4*27 (3)");
nasm("stw 28,4*28 (3)");
nasm("stw 29,4*29 (3)");
nasm("stw 30,4*30 (3)");
nasm("stw 31,4*31 (3)");
nasm("mflr 4");
nasm("mfcr 5");
nasm("mfcr 6");
nasm("stw 4,4*32 (3)");
nasm("stw 5,4*33 (3)");
nasm("stw 6,4*34 (3)");

nasm("bl hook_main");
//应该不使用浮点数
nasm("hook_exit:");
nasm("lwz 4,4*32  (3)");
nasm("lwz 5,4*33  (3)");
nasm("lwz 6,4*34  (3)");
nasm("mtlr 4");
nasm("mtcr 5");
nasm("mtctr 6");
nasm("lwz 1,4*1   (3)");
nasm("lwz 2,4*2   (3)");
nasm("lwz 4,4*4   (3)");
nasm("lwz 5,4*5   (3)");
nasm("lwz 6,4*6   (3)");
nasm("lwz 7,4*7   (3)");
nasm("lwz 8,4*8   (3)");
nasm("lwz 9,4*9   (3)");
nasm("lwz 10,4*10 (3)");
nasm("lwz 11,4*11 (3)");
nasm("lwz 12,4*12 (3)");
nasm("lwz 13,4*13 (3)");
nasm("lwz 14,4*14 (3)");
nasm("lwz 15,4*15 (3)");
nasm("lwz 16,4*16 (3)");
nasm("lwz 17,4*17 (3)");
nasm("lwz 18,4*18 (3)");
nasm("lwz 19,4*19 (3)");

nasm("lwz 20,4*20 (3)");
nasm("lwz 21,4*21 (3)");
nasm("lwz 22,4*22 (3)");
nasm("lwz 23,4*23 (3)");
nasm("lwz 24,4*24 (3)");
nasm("lwz 25,4*25 (3)");
nasm("lwz 26,4*26 (3)");
nasm("lwz 27,4*27 (3)");
nasm("lwz 28,4*28 (3)");
nasm("lwz 29,4*29 (3)");

nasm("lwz 30,4*30 (3)");
nasm("lwz 31,4*31 (3)");
nasm("lwz 3,4*3   (3)");    //最后一个
nasm("blr");
}


static void load_hook_list(){
    int fd = IOS_Open(HOOK_LIST,IPC_OPEN_READ);
    if(fd<0){
        //不存在hook列表
        return;
    }
    u8 buf[1024];
    u8 full_path[1024];
    u8 *path_name = strcpy(full_path,GAME_PATH);
    int str_offset = 0;
    int end_offset = 0;
    int read_offset = 0;
    fd_read_t f;
    //xmemset(&f,0,sizeof(f));
    f.fd = fd;
    f.isend = 0;
    f.offset = 0;
    f.readlen = 0;

    while(1){
        /*hook.txt格式为,每一行
        hex  file
        如果遇到';'则跳过这一行
        */
        int sz = fd_gets(&f,buf);
        if(sz==0){
            break;
        }
        else if(sz<3){
            //被当成是没有数据
        }
        else{
            char*path = 0;
            u32 addr;
            if(get_hook(buf,&addr,&path)){
                //存在文件
                strcpy(path_name,path);
                int fd = IOS_Open(full_path,IPC_OPEN_READ);
                if(fd>0){
                    //文件
                    fd_readbuf(fd,(u8*)addr);
                    IOS_Close(fd);
                }
            }
        }
    }
    IOS_Close(f.fd);
}


static void hook_addr_ex(u32 addr,void* dest,int lr){
    s32 offset = (s32)dest;
    offset -= (s32)addr;
    /*
    0x48 00 00 00 为bl指令
    */
    offset &= ~3;       //4对齐
    if(lr){
        offset += 1;        //bl的位为1
    }
    else{
        //不处理
    }
    offset &= (1024*1024*32) - 1;   //最大跳转为+-32MB
    u32 insn = 0x48000000;
    if(offset<0){
        //负数跳转
        insn += 0x02000000;
    }
    insn |= offset;
    u32 *p = (u32*)addr;
    *p = insn;
}

static void hook_addr(u32 addr,void* dest){
    hook_addr_ex(addr,dest,1);
}

static void hook_b(u32 addr,void* dest){
    hook_addr_ex(addr,dest,0);
}

static void hook_nop(u32 addr){
    u32 *p = (u32*)addr;
    *p = 0x60000000;
}


typedef struct{
    uint16_t    offset;
    //uint16_t    save_offset;
}font_table_t;

typedef struct{
    u8      data[0x120];
}font_data_t;       //字模数据

typedef struct{
    int          alloc_num;
    font_table_t table[0xf34-0xe30];
    font_data_t  data[0xf34-0xe30];
}font_sys_t;

font_sys_t font_sys;

static int read_font(void*dest,int code){
    //GB的字模
    if(SAVE->font_fd>0){
        //先获得code编码的
        int byte1 = (code>>8) & 0xff;       //0xb0 ~ 0xef
        int byte2 = (code) & 0xff;          //
        int offset = byte1 - 0xb0;
        offset += (byte2 - 0xa1) * (0xf7-0xb0);
        offset *= 0x120;
        IOS_Seek(SAVE->font_fd,offset,0);
        //读取缓存数据
        fd_readbuf_sz(SAVE->font_fd,dest,0x120);
    }
}


/*
由于最多使用
0x111ba0 / 0x120
    =   3893  (只能存放这么多0xf35)
当同一个屏幕，应该不会同时显示超过255个文字吧
    所以把
        0xe30 ~ 0xf35 被设置为临时文字显示
*/
static get_font_offset(hook_cpu_t *cpu){
    char *text = 0;
    if(cpu->reg[LR]==0x8000D304){
        //text只能从0x80ffffe0获得
        text = *(char**)(0x80ffffe0);
    }

    //如果alloc_num 大于0x100，则重新分配
    if(font_sys.alloc_num>=0x100){
        font_sys.alloc_num = 0;
    }
    if(cpu->reg[3]==0x8C88){
        //DEBUG;
    }

    int ret;
    if(is_gb(text)){
        int code = cpu->reg[3];
        int i;
        for(i=0;i<font_sys.alloc_num;i++){
            //由于gb编码不可能小于 0xe30,所以可以用 table.offset替代
            if(font_sys.table[i].offset==code){
                //找到需要偏移的量
                ret = 0xe30 + i;
                break;
            }
        }

        if(i>=font_sys.alloc_num){
            //没有找到
            ret = 0xe30 + font_sys.alloc_num;
            font_sys.table[font_sys.alloc_num].offset = code;

            //从字库文件中读取数据
            read_font(
                (void*)(0x92526060+0x120*ret),
                code
            );
            font_sys.alloc_num++;
        }
    }
    else{
        ret = CALL(0x8000C600,cpu->reg[3],cpu->reg[4]);
        if(ret>=0xe30){
            //临时使用的字模
            int i;
            for(i=0;i<font_sys.alloc_num;i++){
                if(font_sys.table[i].offset==ret){
                    //找到需要偏移的量
                    ret += i;
                    break;
                }
            }

            if(i>=font_sys.alloc_num){
                //未找到，申请一个字模缓存
                ret += font_sys.alloc_num;
                font_sys.table[font_sys.alloc_num].offset = ret;
                //复制数据到目标内
                memcpy(
                    (void*)(0x92526060+0x120*ret),
                    font_sys.data + font_sys.alloc_num,
                    0x120
                );
                font_sys.alloc_num++;
            }
        }
    }
    cpu->reg[3] = ret;
}

void _jmp_func(){
#if 1
nasm(".section	\".text\"");
nasm(".align 2");
nasm(".globl JMP_FUNC");
nasm(".type	JMP_FUNC, @function");
nasm("JMP_FUNC:");
nasm("mtctr 3");
nasm("lwz 3,4(4)");
nasm("lwz 5,12(4)");
nasm("lwz 6,16(4)");
nasm("lwz 0,0(4)");     //lr 寄存器
nasm("lwz 1,20(4)");    //sp 寄存器
//作为最后一个读取
nasm("lwz 4,8(4)");
nasm("mtlr 0");     //设置已经的lr
//返回到目标,以ctr寄存器返回，lr寄存器为上一个保存的地址
//转为内部执行
nasm("bctr ");

//使icache失效，(执行缓存)
nasm("icache_flush:");
nasm("mfspr 0,1008");   //获得hid0值
nasm("ori 0,0,1<<11");    //设置icfi
nasm("mtspr 1008,0");
nasm("blr");
#endif
}

static int get_hook(char*buf,u32*addr,char**path){
    int offset = 0;
    offset = skip(buf,offset);
    u32 value = 0;
    while(1){
        int v = hex[buf[offset]];
        if(!v) break;   //非16进制
        offset++;
        value <<= 4;
        value |= v - 1;
    }
    //跳过空格或者tab
    offset = skip(buf,offset);
    if(value==0) return 0;
    *addr = value;
    *path = buf + offset;
    return 1;
}
